﻿using MyAttribute.Extend;
using MyAttribute.ModelValidate;
using System.Reflection;

namespace MyAttribute
{
    //1.特性Attibute的本质
    //2.和注释有什么区别
    //3.声明Attribute，AttributeUsage
    //4.运行中获取Attribute：
    //5.特性获取额外信息
    //6.特性获取额外功能
    //7.Attribute的实战应有
    internal class Program
    {
        static void Main(string[] args)
        {
            //一、什么是特性？
            //一个类，直接或者是间接的继承自抽象类 Attribute, 可以把这个东西，使用[] 进行注册标记到类以及类内部的所有的成员；约定俗成，默认以Attribute结尾，在注册标记的时候，如果特性类是Attribute结尾的，可以省略不写结尾的Atrribute;

            //AttributeUsage: 用来约束特性的的特性；约束听可以应用在哪些成员；
            //  AttributeUsage:AllowMultiple--true---可以标记多个， false，只能标记一次
            //  AttributeUsage:Inherited---true---标记的特性，可以被继承到子类；否则不能被继承； 
            //特性和注释有啥区别？ 注释--在编译器编译后，不存在；

            //二、到底有什么价值呢，在底层到底是什么呢？
            //反编译：在类里面生成一堆Ctor的东西，好像都调用不到，好像没什么卵用~ 
            Student student = new Student()
            {
                Id = 123,
                Name = "黑猫~~",
                Description = "高级班的VIP学员",
            };
            //student.ctor()//调用不到 
            //标记好的特性，到底如何调用？   
            //要调用特性，必须要用反射，如果没有反射，特性还真是没有用处~~

            //1.使用反射 
            #region 通过反射，可以获取到标记在任何地方的特性

            //Type type = typeof(Student);
            Type type = student.GetType();
            //type.GetCustomAttributes(true) //获取标记在这个类上的所有的特性；
            //2.获取特性实例的标准用法---先判断，然后获取实例
            //if (type.IsDefined(typeof(CustomAttribute),true)) //先判断，再获取实例
            //{
            //    CustomAttribute  attribute = type.GetCustomAttribute<CustomAttribute>(); //执行特性的构造函数 
            //}

            //获取属性上的特性
            foreach (var prop in type.GetProperties())
            {
                if (prop.IsDefined(typeof(CustomAttribute), true))
                {
                    CustomAttribute attribute = prop.GetCustomAttribute<CustomAttribute>();
                }
            }
            //获取字段上的特性
            foreach (var field in type.GetFields())
            {
                if (field.IsDefined(typeof(CustomAttribute), true))
                {
                    CustomAttribute attribute = field.GetCustomAttribute<CustomAttribute>();
                }
            }

            //获取方法上的特性
            foreach (var mehtod in type.GetMethods())
            {
                if (mehtod.IsDefined(typeof(CustomAttribute), true))
                {
                    CustomAttribute attribute = mehtod.GetCustomAttribute<CustomAttribute>();
                }
            }
            #endregion
            //特性是一个类，获取到一个实例，--就是得到了一个类的实例
            //该怎么做？  大家思考下，现在是20：49,中场休息5分钟，20:55继续~~ 期间老师不说话了~~

            //三、特性到底可以带来什么？
            //1. 特性可以提供额外信息，----本来不具备这个信息的，可以通过特性来增加信息；
            //2. 特性可以提供额外的功能，-----本来不具备这个功能的，可以通过特性来支持这个功能给；


            #region 特性获取额外信息
            {
                UserInfo user = new UserInfo()
                {
                    Id = 123,
                    Name = "水手怕水",
                    Age = 25,
                    Mobile = "18888888888",
                    QQ = 18888888888,
                    State = UserStateEnum.Frozen
                };
                {
                    //实体中的数据---展示出来，对于状态信息，如果我希望给展示出来；
                    if (user.State == UserStateEnum.Normal)
                    {
                        Console.WriteLine("状态：正常");
                    }
                    else if (user.State == UserStateEnum.Frozen)
                    {
                        Console.WriteLine("状态：已冻结");
                    }
                    else if (user.State == UserStateEnum.Deleted)
                    {
                        Console.WriteLine("状态：已删除");
                    }
                }

                //可能会有很多个页面要展示这个状态；
                //1.描述的文字信息需要变化下；---有很多的页面需要调整---费劲；
                //
                //特性 ：
                {
                    Type type1 = typeof(UserStateEnum);
                    FieldInfo field = type1.GetField(user.State.ToString());
                    if (field.IsDefined(typeof(RemarkAttribute), true))
                    {
                        RemarkAttribute attribute = field.GetCustomAttribute<RemarkAttribute>();
                        string description = attribute.GetDescription();
                        Console.WriteLine(description);
                    }

                    user.State = UserStateEnum.Normal;
                    string desription1 = DescriptionManager.GetDescription(user.State);
                    string desription11 = user.State.GetDescription();//扩展方法

                    user.State = UserStateEnum.Frozen;
                    string desription2 = DescriptionManager.GetDescription(user.State);
                    string desription22 = user.State.GetDescription();//扩展方法

                    user.State = UserStateEnum.Deleted;
                    string desription3 = DescriptionManager.GetDescription(user.State);
                    string desription33 = user.State.GetDescription();//扩展方法

                    ////int i;
                    ////i.GetDescription();  //如果扩展方法里面的参数类型是object那么int类型也可以调用  这样是不对的。

                }
            }
            #endregion

            
            #region 特性获取额外功能
            {
                UserInfo user = new UserInfo()
                {
                    Id = 234,
                    Name = "小城大事小城大事小城大事",
                    QQ = 99999999999,
                    Mobile = "19999999999999",
                    Age = 25,
                    State = UserStateEnum.Normal
                };
                //页面收集到的数据要保存到数据库中去；
                //字段有特定的长度； 实体中给定的数据，不能超过数据库的字段的长度；保存不了   错误：截断二进制~~ 
                //在保存数据之前，就要验证下； 
                if (user.Mobile.Length != 11)
                {
                    Console.WriteLine("对不起，手机号不规范");
                }
                else if (user.Name.Length > 10)
                {
                    Console.WriteLine("对不起，名称超长了~~");
                }

                //如果增加了几个字段； 
                //怎么办？ 特性完成实体验证逻辑， 没有这个功能，通过特性来增加~

                //验证分类：
                //1.验证长度----------定义特性
                //2.验证数字的大小--- 定义特性
                //3.验证手机号的格式~~定义特性

                #region 可以把这部分封装起来
                {
                    Type type1 = user.GetType();
                    foreach (var prop in type1.GetProperties())
                    {
                        if (prop.IsDefined(typeof(CustomLengthAttribute), true))
                        {
                            CustomLengthAttribute attribute = prop.GetCustomAttribute<CustomLengthAttribute>();
                            object oValue = prop.GetValue(user);
                            bool bReulst = attribute.Validate(oValue);
                            if (bReulst)
                            {
                                Console.WriteLine("Ok");
                            }
                            else
                            {
                                Console.WriteLine(attribute.ErrorMessage);
                            }

                            CustomRequiredAttribute attribute1 = prop.GetCustomAttribute<CustomRequiredAttribute>();

                            bool bReulst1 = attribute1.Validate(oValue);
                            if (bReulst1)
                            {
                                Console.WriteLine("Ok");
                            }
                            else
                            {
                                Console.WriteLine(attribute.ErrorMessage);
                            }
                        }
                    } 
                    #endregion
                }
                {
                    List<(bool, string)> validatelist = ValidateManger.Validate<UserInfo>(user);
                }
                //通过特性增加了额外的功能；
                //定义了一个个规则，还有一些扩展点，可以让用户自己来定义规则； 
            } 
            #endregion
            Console.ReadKey();
        }
    }
}
